package com.example.examsystem.service;

import com.example.examsystem.dto.NoticeDto;
import com.example.examsystem.dto.PageResponse;
import com.example.examsystem.exception.BusinessException;
import com.example.examsystem.model.Notice;
import com.example.examsystem.model.User;
import com.example.examsystem.repository.NoticeRepository;
import com.example.examsystem.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 通知公告服务
 * 处理通知相关的业务逻辑
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class NoticeService {
    
    private final NoticeRepository noticeRepository;
    private final UserRepository userRepository;
    
    /**
     * 分页查询通知列表
     * @param page 页码
     * @param size 每页大小
     * @param title 标题
     * @param type 类型
     * @param status 状态
     * @param targetType 目标类型
     * @param isTop 是否置顶
     * @param isUrgent 是否紧急
     * @param createdBy 创建者ID
     * @return 分页通知数据
     */
    public PageResponse<NoticeDto> getNotices(int page, int size, String title, String type, String status,
                                             String targetType, Boolean isTop, Boolean isUrgent, Long createdBy) {
        log.debug("查询通知列表 - 页码: {}, 大小: {}, 筛选条件: 标题={}, 类型={}, 状态={}, 目标类型={}, 置顶={}, 紧急={}, 创建者={}", 
                page, size, title, type, status, targetType, isTop, isUrgent, createdBy);
        
        Pageable pageable = PageRequest.of(page, size, 
            Sort.by("isTop").descending()
                .and(Sort.by("isUrgent").descending())
                .and(Sort.by("createdAt").descending()));
        
        Page<Notice> noticePage = noticeRepository.findByQuery(
            title, type, status, targetType, isTop, isUrgent, createdBy, pageable);
        
        List<NoticeDto> content = noticePage.getContent().stream()
            .map(this::convertToDto)
            .collect(Collectors.toList());
        
        return new PageResponse<>(
            content,
            noticePage.getNumber(),
            noticePage.getSize(),
            noticePage.getTotalElements(),
            noticePage.getTotalPages()
        );
    }
    
    /**
     * 获取通知详情
     * @param id 通知ID
     * @return 通知详情
     */
    @Transactional
    public NoticeDto getNotice(Long id) {
        log.debug("获取通知详情 - ID: {}", id);
        Notice notice = noticeRepository.findById(id)
            .orElseThrow(() -> new BusinessException("通知不存在"));
        
        // 增加浏览次数
        noticeRepository.incrementViewCount(id);
        
        return convertToDto(notice);
    }
    
    /**
     * 创建通知
     * @param noticeDto 通知信息
     * @param creatorId 创建者ID
     * @return 创建的通知
     */
    @Transactional
    public NoticeDto createNotice(NoticeDto noticeDto, Long creatorId) {
        log.debug("创建通知 - 标题: {}, 创建者: {}", noticeDto.getTitle(), creatorId);
        
        Notice notice = new Notice();
        notice.setTitle(noticeDto.getTitle());
        notice.setContent(noticeDto.getContent());
        notice.setType(noticeDto.getType());
        notice.setStatus("DRAFT");
        notice.setTargetType(noticeDto.getTargetType());
        notice.setTargetIds(noticeDto.getTargetIds());
        notice.setIsTop(noticeDto.getIsTop() != null ? noticeDto.getIsTop() : false);
        notice.setIsUrgent(noticeDto.getIsUrgent() != null ? noticeDto.getIsUrgent() : false);
        notice.setPublishTime(noticeDto.getPublishTime());
        notice.setExpireTime(noticeDto.getExpireTime());
        notice.setCreatedBy(creatorId);
        notice.setCreatedAt(LocalDateTime.now());
        notice.setUpdatedAt(LocalDateTime.now());
        
        Notice savedNotice = noticeRepository.save(notice);
        log.info("通知创建成功 - ID: {}, 标题: {}", savedNotice.getId(), savedNotice.getTitle());
        
        return convertToDto(savedNotice);
    }
    
    /**
     * 更新通知
     * @param id 通知ID
     * @param noticeDto 通知信息
     * @return 更新的通知
     */
    @Transactional
    public NoticeDto updateNotice(Long id, NoticeDto noticeDto) {
        log.debug("更新通知 - ID: {}", id);
        
        Notice notice = noticeRepository.findById(id)
            .orElseThrow(() -> new BusinessException("通知不存在"));
        
        if ("PUBLISHED".equals(notice.getStatus())) {
            throw new BusinessException("已发布的通知不能修改");
        }
        
        notice.setTitle(noticeDto.getTitle());
        notice.setContent(noticeDto.getContent());
        notice.setType(noticeDto.getType());
        notice.setTargetType(noticeDto.getTargetType());
        notice.setTargetIds(noticeDto.getTargetIds());
        notice.setIsTop(noticeDto.getIsTop() != null ? noticeDto.getIsTop() : false);
        notice.setIsUrgent(noticeDto.getIsUrgent() != null ? noticeDto.getIsUrgent() : false);
        notice.setPublishTime(noticeDto.getPublishTime());
        notice.setExpireTime(noticeDto.getExpireTime());
        notice.setUpdatedAt(LocalDateTime.now());
        
        Notice updatedNotice = noticeRepository.save(notice);
        log.info("通知更新成功 - ID: {}", id);
        
        return convertToDto(updatedNotice);
    }
    
    /**
     * 删除通知
     * @param id 通知ID
     */
    @Transactional
    public void deleteNotice(Long id) {
        log.debug("删除通知 - ID: {}", id);
        
        Notice notice = noticeRepository.findById(id)
            .orElseThrow(() -> new BusinessException("通知不存在"));
        
        if ("PUBLISHED".equals(notice.getStatus())) {
            throw new BusinessException("已发布的通知不能删除");
        }
        
        noticeRepository.deleteById(id);
        log.info("通知删除成功 - ID: {}", id);
    }
    
    /**
     * 发布通知
     * @param id 通知ID
     * @return 发布的通知
     */
    @Transactional
    public NoticeDto publishNotice(Long id) {
        log.debug("发布通知 - ID: {}", id);
        
        Notice notice = noticeRepository.findById(id)
            .orElseThrow(() -> new BusinessException("通知不存在"));
        
        if ("PUBLISHED".equals(notice.getStatus())) {
            throw new BusinessException("通知已发布");
        }
        
        notice.setStatus("PUBLISHED");
        if (notice.getPublishTime() == null) {
            notice.setPublishTime(LocalDateTime.now());
        }
        notice.setUpdatedAt(LocalDateTime.now());
        
        Notice publishedNotice = noticeRepository.save(notice);
        log.info("通知发布成功 - ID: {}", id);
        
        return convertToDto(publishedNotice);
    }
    
    /**
     * 归档通知
     * @param id 通知ID
     * @return 归档的通知
     */
    @Transactional
    public NoticeDto archiveNotice(Long id) {
        log.debug("归档通知 - ID: {}", id);
        
        Notice notice = noticeRepository.findById(id)
            .orElseThrow(() -> new BusinessException("通知不存在"));
        
        notice.setStatus("ARCHIVED");
        notice.setUpdatedAt(LocalDateTime.now());
        
        Notice archivedNotice = noticeRepository.save(notice);
        log.info("通知归档成功 - ID: {}", id);
        
        return convertToDto(archivedNotice);
    }
    
    /**
     * 批量操作通知
     * @param ids 通知ID列表
     * @param action 操作类型（publish, archive, delete）
     * @return 操作数量
     */
    @Transactional
    public int batchOperateNotices(List<Long> ids, String action) {
        log.debug("批量操作通知 - 操作: {}, 数量: {}", action, ids.size());
        
        switch (action) {
            case "publish":
                List<Notice> draftNotices = noticeRepository.findAllById(ids)
                    .stream()
                    .filter(notice -> "DRAFT".equals(notice.getStatus()))
                    .collect(Collectors.toList());
                
                LocalDateTime now = LocalDateTime.now();
                for (Notice notice : draftNotices) {
                    notice.setStatus("PUBLISHED");
                    if (notice.getPublishTime() == null) {
                        notice.setPublishTime(now);
                    }
                    notice.setUpdatedAt(now);
                }
                noticeRepository.saveAll(draftNotices);
                log.info("批量发布通知成功 - 数量: {}", draftNotices.size());
                return draftNotices.size();
                
            case "archive":
                noticeRepository.batchUpdateStatus(ids, "ARCHIVED", LocalDateTime.now());
                log.info("批量归档通知成功 - 数量: {}", ids.size());
                return ids.size();
                
            case "delete":
                List<Notice> deletableNotices = noticeRepository.findAllById(ids)
                    .stream()
                    .filter(notice -> !"PUBLISHED".equals(notice.getStatus()))
                    .collect(Collectors.toList());
                
                noticeRepository.deleteAll(deletableNotices);
                log.info("批量删除通知成功 - 数量: {}", deletableNotices.size());
                return deletableNotices.size();
                
            default:
                throw new BusinessException("不支持的操作类型: " + action);
        }
    }
    
    /**
     * 获取用户可见的通知
     */
    public PageResponse<NoticeDto> getVisibleNotices(Long userId, int page, int size, String type) {
        log.debug("获取用户可见通知 - 用户ID: {}, 页码: {}, 大小: {}, 类型: {}", userId, page, size, type);
        
        // 获取用户主要角色（简化处理）
        String userRole = "STUDENT"; // 简化处理，实际应该从用户角色关联表获取
        
        Pageable pageable = PageRequest.of(page, size, 
            Sort.by("isTop").descending()
                .and(Sort.by("isUrgent").descending())
                .and(Sort.by("publishTime").descending()));
        
        Page<Notice> noticePage = noticeRepository.findVisibleNotices(
            userId, userRole, "PUBLISHED", LocalDateTime.now(), pageable);
        
        List<NoticeDto> content = noticePage.getContent().stream()
            .filter(notice -> type == null || type.isEmpty() || type.equals(notice.getType()))
            .map(this::convertToDto)
            .collect(Collectors.toList());
        
        return new PageResponse<>(
            content,
            noticePage.getNumber(),
            noticePage.getSize(),
            noticePage.getTotalElements(),
            noticePage.getTotalPages()
        );
    }
    
    /**
     * 获取置顶通知
     * @return 置顶通知列表
     */
    public List<NoticeDto> getTopNotices() {
        log.debug("获取置顶通知");
        List<Notice> topNotices = noticeRepository.findByIsTopTrueAndStatusOrderByCreatedAtDesc("PUBLISHED");
        return topNotices.stream()
            .map(this::convertToDto)
            .collect(Collectors.toList());
    }
    
    /**
     * 获取紧急通知
     * @return 紧急通知列表
     */
    public List<NoticeDto> getUrgentNotices() {
        log.debug("获取紧急通知");
        List<Notice> urgentNotices = noticeRepository.findByIsUrgentTrueAndStatusOrderByCreatedAtDesc("PUBLISHED");
        return urgentNotices.stream()
            .map(this::convertToDto)
            .collect(Collectors.toList());
    }
    
    /**
     * 自动归档过期通知（定时任务）
     */
    @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
    @Transactional
    public void autoArchiveExpiredNotices() {
        log.debug("执行自动归档过期通知任务");
        int count = noticeRepository.autoArchiveExpiredNotices(LocalDateTime.now());
        log.info("自动归档过期通知完成 - 归档数量: {}", count);
    }
    
    /**
     * 转换为DTO
     * @param notice 通知实体
     * @return 通知DTO
     */
    private NoticeDto convertToDto(Notice notice) {
        NoticeDto dto = new NoticeDto();
        dto.setId(notice.getId());
        dto.setTitle(notice.getTitle());
        dto.setContent(notice.getContent());
        dto.setType(notice.getType());
        dto.setStatus(notice.getStatus());
        dto.setTargetType(notice.getTargetType());
        dto.setTargetIds(notice.getTargetIds());
        dto.setIsTop(notice.getIsTop());
        dto.setIsUrgent(notice.getIsUrgent());
        dto.setPublishTime(notice.getPublishTime());
        dto.setExpireTime(notice.getExpireTime());
        dto.setViewCount(notice.getViewCount());
        dto.setCreatedBy(notice.getCreatedBy());
        dto.setCreatedAt(notice.getCreatedAt());
        dto.setUpdatedAt(notice.getUpdatedAt());
        
        // 设置创建者信息
        if (notice.getCreator() != null) {
            dto.setCreatorUsername(notice.getCreator().getUsername());
            dto.setCreatorFullName(notice.getCreator().getFullName());
        }
        
        return dto;
    }
}
